package com.lry.pda.bottom.mysql.frontend;

import java.util.*;

//SELECT_SYM /*select关键字*/
//select_item_list /*select项列表*/
//opt_from_clause /*可选：from子句*/
//opt_where_clause /*可选：where子句*/
//opt_group_clause /*可选：group子句*/
//ORDER BY    id,create_by desc
/**
 * 0：select_stmt -> query_expression
 * 1：query_expression -> query_expression_body opt_order_clause
 *
 * 2：query_expression_body -> SELECT_SYM select_item_list from_clause opt_where_clause
 *
 * 3：select_item_list -> *
 * 4：select_item_list -> field_list
 * 5：field_list -> field field_list_term
 * 6：field_list_term -> comma field_list | 空
 *
 * 7：from_clause -> FROM_SYM table
 *
 * 8：opt_where_clause -> opt_group_clause
 * 9：opt_where_clause -> WHERE_SYM where_clause opt_group_clause
 * 10：where_clause -> field compare where_val where_clause_term
 * 11：where_clause_term -> AND_SYM where_clause | 空
 *
 * 12：compare -> eq
 * 13：compare -> ge
 * 14：compare -> gt
 * 15：compare -> le
 * 16：compare -> lt
 * 17：compare -> in
 *
 * 18：opt_group_clause -> GROUP_BY_SYM field_list | 空
 *
 * 19：opt_order_clause -> opt_limit_clause
 * 20：opt_order_clause -> ORDER_SYM BY_SYM order_item opt_limit_clause
 * 21：order_item -> field_list order_asc_desc
 * 22：order_asc_desc -> ASC_SYM  | 空
 * 23：order_asc_desc -> DESC_SYM | 空
 *
 * 24：opt_limit_clause -> LIMIT_SYM num comma num  | 空
 */
public class Grammar {

    public static int QUERY_EXPR_TO_SELECT_STMT = -1;
    public static int QUERY_EXPR_BODY_TO_QUERY_EXPR = -1;
    public static int TO_QUERY_EXPR_BODY = -1;
    public static int TIMES_TO_SELECT_ITEM_LIST = -1;
    public static int FIELD_LIST_TO_SELECT_ITEM_LIST = -1;
    public static int FIELD_TO_FIELD_LIST = -1;
    public static int FIELD_LIST_TO_FIELD_LIST_TERM = -1;
    public static int FROM_SYM_TO_FROM_CLAUSE = -1;
    public static int OPT_GROUP_CLAUSE_TO_OPT_WHERE_CLAUSE = -1;
    public static int WHERE_SYM_TO_OPT_WHERE_CLAUSE = -1;
    public static int FIELD_TO_WHERE_CLAUSE = -1;
    public static int AND_TO_WHERE_CLAUSE_TERM = -1;
    public static int EQ_TO_COMPARE = -1;
    public static int GE_TO_COMPARE = -1;
    public static int GT_TO_COMPARE = -1;
    public static int LE_TO_COMPARE = -1;
    public static int LT_TO_COMPARE = -1;
    public static int IN_TO_COMPARE = -1;
    public static int GROUP_SYM_TO_OPT_GROUP_CLAUSE = -1;
    public static int OPT_LIMIT_CLAUSE_TO_OPT_ORDER_CLAUSE = -1;
    public static int ORDER_SYM_TO_OPT_ORDER_CLAUSE = -1;
    public static int FIELD_LIST_TO_ORDER_ITEM = -1;
    public static int ASC_SYM_TO_ORDER_ASC_DESC = -1;
    public static int DESC_SYM_TO_ORDER_ASC_DESC = -1;
    public static int LIMIT_SYM_TO_OPT_LIMIT_CLAUSE = -1;

    private Map<Integer, Symbols> symbolMap = new HashMap<>();
    private Map<Integer, List<Production>> productionMap = new HashMap<>();

    private static Grammar self;

    private Grammar(){
        initSelectGrammar();
        addTerminal();
    }


    private void initSelectGrammar() {
        int number = 0;
        QUERY_EXPR_TO_SELECT_STMT = number;
        addProduction(false,new Production(0, MysqlTokenType.SELECT_STMT.ordinal(), Arrays.asList(MysqlTokenType.QUERY_EXPR.ordinal()),number++));

        QUERY_EXPR_BODY_TO_QUERY_EXPR = number;
        addProduction(false,new Production(0, MysqlTokenType.QUERY_EXPR.ordinal(), Arrays.asList(MysqlTokenType.QUERY_EXPR_BODY.ordinal(),MysqlTokenType.OPT_ORDER_CLAUSE.ordinal()),number++));

        TO_QUERY_EXPR_BODY = number;
        addProduction(false,new Production(0, MysqlTokenType.QUERY_EXPR_BODY.ordinal(), Arrays.asList(MysqlTokenType.SELECT_SYM.ordinal(),MysqlTokenType.SELECT_ITEM_LIST.ordinal(),MysqlTokenType.FROM_CLAUSE.ordinal(),MysqlTokenType.OPT_WHERE_CLAUSE.ordinal()),number++));

        TIMES_TO_SELECT_ITEM_LIST = number;
        addProduction(false,new Production(0, MysqlTokenType.SELECT_ITEM_LIST.ordinal(), Arrays.asList(MysqlTokenType.TIMES.ordinal()),number++));

        FIELD_LIST_TO_SELECT_ITEM_LIST = number;
        addProduction(false,new Production(0, MysqlTokenType.SELECT_ITEM_LIST.ordinal(), Arrays.asList(MysqlTokenType.FIELD_LIST.ordinal()),number++));

        FIELD_TO_FIELD_LIST = number;
        addProduction(false,new Production(0, MysqlTokenType.FIELD_LIST.ordinal(), Arrays.asList(MysqlTokenType.FIELD.ordinal(),MysqlTokenType.FIELD_LIST_TERM.ordinal()),number++));

        FIELD_LIST_TO_FIELD_LIST_TERM = number;
        addProduction(true,new Production(0, MysqlTokenType.FIELD_LIST_TERM.ordinal(), Arrays.asList(MysqlTokenType.COMMA.ordinal(),MysqlTokenType.FIELD_LIST.ordinal()),number++));

        FROM_SYM_TO_FROM_CLAUSE = number;
        addProduction(false,new Production(0, MysqlTokenType.FROM_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.FROM_SYM.ordinal(),MysqlTokenType.TABLE.ordinal()),number++));

        OPT_GROUP_CLAUSE_TO_OPT_WHERE_CLAUSE = number;
        addProduction(true,new Production(0, MysqlTokenType.OPT_WHERE_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.OPT_GROUP_CLAUSE.ordinal()),number++));

        WHERE_SYM_TO_OPT_WHERE_CLAUSE = number;
        addProduction(false,new Production(0, MysqlTokenType.OPT_WHERE_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.WHERE_SYM.ordinal(),MysqlTokenType.WHERE_CLAUSE.ordinal(),MysqlTokenType.OPT_GROUP_CLAUSE.ordinal()),number++));

        FIELD_TO_WHERE_CLAUSE = number;
        addProduction(false,new Production(0, MysqlTokenType.WHERE_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.FIELD.ordinal(),MysqlTokenType.COMPARE.ordinal(),MysqlTokenType.WHERE_VAL.ordinal(),MysqlTokenType.WHERE_CLAUSE_TERM.ordinal()),number++));

        AND_TO_WHERE_CLAUSE_TERM = number;
        addProduction(true,new Production(0, MysqlTokenType.WHERE_CLAUSE_TERM.ordinal(), Arrays.asList(MysqlTokenType.AND_SYM.ordinal(),MysqlTokenType.WHERE_CLAUSE.ordinal()),number++));

        EQ_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.EQ.ordinal()),number++));

        GE_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.GE.ordinal()),number++));

        GT_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.GT.ordinal()),number++));

        LE_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.LE.ordinal()),number++));

        LT_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.LT.ordinal()),number++));

        IN_TO_COMPARE = number;
        addProduction(false,new Production(0, MysqlTokenType.COMPARE.ordinal(), Arrays.asList(MysqlTokenType.IN.ordinal()),number++));

        GROUP_SYM_TO_OPT_GROUP_CLAUSE = number;
        addProduction(true,new Production(0, MysqlTokenType.OPT_GROUP_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.GROUP_SYM.ordinal(),MysqlTokenType.BY_SYM.ordinal(),MysqlTokenType.FIELD_LIST.ordinal()),number++));

        OPT_LIMIT_CLAUSE_TO_OPT_ORDER_CLAUSE = number;
        addProduction(true,new Production(0, MysqlTokenType.OPT_ORDER_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.OPT_LIMIT_CLAUSE.ordinal()),number++));

        ORDER_SYM_TO_OPT_ORDER_CLAUSE = number;
        addProduction(false,new Production(0, MysqlTokenType.OPT_ORDER_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.ORDER_SYM.ordinal(),MysqlTokenType.BY_SYM.ordinal(),MysqlTokenType.ORDER_ITEM.ordinal(),MysqlTokenType.OPT_LIMIT_CLAUSE.ordinal()),number++));

        FIELD_LIST_TO_ORDER_ITEM = number;
        addProduction(false,new Production(0, MysqlTokenType.ORDER_ITEM.ordinal(), Arrays.asList(MysqlTokenType.FIELD_LIST.ordinal(),MysqlTokenType.ORDER_ASC_DESC.ordinal()),number++));

        ASC_SYM_TO_ORDER_ASC_DESC = number;
        addProduction(true,new Production(0, MysqlTokenType.ORDER_ASC_DESC.ordinal(), Arrays.asList(MysqlTokenType.ASC_SYM.ordinal()),number++));

        DESC_SYM_TO_ORDER_ASC_DESC = number;
        addProduction(true,new Production(0, MysqlTokenType.ORDER_ASC_DESC.ordinal(), Arrays.asList(MysqlTokenType.DESC_SYM.ordinal()),number++));

        LIMIT_SYM_TO_OPT_LIMIT_CLAUSE = number;
        addProduction(true,new Production(0, MysqlTokenType.OPT_LIMIT_CLAUSE.ordinal(), Arrays.asList(MysqlTokenType.LIMIT_SYM.ordinal(),MysqlTokenType.NUM_OR_ID.ordinal(),MysqlTokenType.COMMA.ordinal(),MysqlTokenType.NUM_OR_ID.ordinal()),number++));
    }

    private void addProduction(boolean nullable, Production production){

        productionMap.compute(production.getLeft(),(k,v)->{
            if(null==v){
                v = new ArrayList<>();
            }
            if(!v.contains(production)){
                v.add(production);
            }
            return v;
        });

        addSymbolMap(nullable,production);
    }

    private void addSymbolMap(boolean nullable, Production production){
        symbolMap.compute(production.getLeft(),(k,v)->{
            if(v==null){
                v = new Symbols(k,nullable);
            }
            v.addProduction(production.getRights());
            return v;
        });
    }

    private void addTerminal(){
        for (int symbol = MysqlTokenType.FIRST_TERMINAL_INDEX; symbol <= MysqlTokenType.LAST_TERMINAL_INDEX; symbol++) {
            symbolMap.put(symbol, new Symbols(symbol, false));
        }
    }

    public Map<Integer, List<Production>> getProductionMap() {
        return productionMap;
    }


    public Map<Integer, Symbols> getSymbolMap() {
        return symbolMap;
    }

    public static Grammar getSelf(){
        if(null==self){
            self = new Grammar();
        }
        return self;
    }


}
